package luj.tool.jnproxyan.proxy.network.socks.actor.root.message.request.connect.finish;

import io.netty.channel.Channel;
import luj.tool.jnproxyan.proxy.network.direct.P2sHttpsInitializer;
import luj.tool.jnproxyan.proxy.network.logger.Proxy2sLogger;
import luj.tool.jnproxyan.proxy.network.socks.actor.root.Socks5Actor;
import luj.tool.jnproxyan.proxy.network.socks.actor.root.message.http.client2p.request.flush.HttpReqFlushMsg;
import luj.tool.jnproxyan.proxy.network.socks.request.RequestBody;
import luj.tool.jnproxyan.proxy.network.socks.request.SocksRequest;
import luj.tool.jnproxyan.proxy.network.socks.request.op.RequestByIdGetter;
import luj.tool.jnproxyan.proxy.stats.connect.ConnectFailAdder;
import luj.tool.jnproxyan.proxy.stats.connect.ConnectStats;
import luj.tool.jnproxyan.proxy.stats.connect.StatsItemGetter;
import luj.tool.jnproxyan.framework.proxy.actor.tell.ActorTeller;
import luj.tool.jnproxyan.proxy.util.data.DataDirtySaver;
import luj.tool.jnproxyan.proxy.util.data.session.DataUsingStarter;
import luj.tool.jnproxyan.proxy.util.data.wrap.ResultGlobalLWrapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * 连上远端网站后的处理逻辑
 *
 * @see luj.tool.jnproxyan.proxy.network.socks.actor.root.message.request.connect.OnReqConnect#onHandle
 */
@Component
final class OnConnectFinish implements Socks5Actor.Handler<ConnectFinishMsg> {

  @Override
  public void onHandle(Context ctx) throws Exception {
    ConnectFinishMsg msg = ctx.getMessage(this);
    var result = msg.connectResult2();

    if (!result.isSuccess()) {
      handleFail(ctx);
      return;
    }

    Socks5Actor self = ctx.getActorState(this);
    SocksRequest socks = getRequest(self, msg);
    RequestBody reqBody = socks.getBody();

    Long reqId = socks.getReqId();
    new Proxy2sLogger(LOG, reqId).log("连<站>成功：{}:{}", reqBody.getTargetHost(), reqBody.getTargetPort());

    Channel conn = result.channel();
    socks.setP2sConn3(conn);

    Ref selfRef = ctx.getActorRef();
    _httpsInit.inject(conn, socks, self.getHttpHistoryDat(), selfRef).init();
    socks.setStatus("#3:连接网站成功");

    // 连接比较慢时，会堆积一些请求在队列，及时触发消化
    _actorTeller.inject(selfRef).tell(HttpReqFlushMsg.class, (b, m) -> b
        .f(m::reqId).set(reqId)
    );

    // db
    updateStats(ctx);
  }

  private void updateStats(Context ctx) {
    Socks5Actor self = ctx.getActorState(this);
    ConnectFinishMsg msg = ctx.getMessage(this);

    DataUsingStarter.Session data = _dataUser.inject(self).start();
    var stats = _globalLoader.load(data).andWrap(ConnectStats.class);

    ConnectStats.Item item = _itemGetter.inject(stats, msg.targetHost(), data).getOrInit();
    item.requestTotal().add(1);

//    new ConnectResultUpdater(stats, msg.reqId(), HistoryItem.Result.SUCCESS).update();
    _dirtySaver.inject(data).save();
  }

  private void handleFail(Context ctx) {
    Socks5Actor self = ctx.getActorState(this);
    ConnectFinishMsg msg = ctx.getMessage(this);
    SocksRequest request = getRequest(self, msg);

//    // socks5
//    ConnectionAcceptInitializer.Connection c2pConn = request.getC2pConn();
//    c2pConn.write(new SocksReplyMaker(c2pConn).make(0x04));
//    c2pConn.close();

    Throwable error = msg.connectResult2().cause();
    request.setStatus("#3:" + error.getClass().getSimpleName());
    LOG.debug("连网站失败：#{} - {}", request.getReqId(), error.getMessage());

    // db
    DataUsingStarter.Session data = _dataUser.inject(self).start();
    var stats = _globalLoader.load(data).andWrap(ConnectStats.class);

//    new ConnectResultUpdater(stats, msg.reqId(), HistoryItem.Result.FAIL).update();
    _failAdder.inject(stats, msg.targetHost(), msg.targetPort(), data).add();

    _dirtySaver.inject(data).save();
  }

  private SocksRequest getRequest(Socks5Actor self, ConnectFinishMsg msg) {
    return _reqGetter.inject(self, msg.reqId()).getReq();
  }

  private static final Logger LOG = LoggerFactory.getLogger(OnConnectFinish.class);

  @Autowired
  RequestByIdGetter _reqGetter;

  @Autowired
  DataUsingStarter _dataUser;

  @Autowired
  ResultGlobalLWrapper _globalLoader;

  @Autowired
  DataDirtySaver _dirtySaver;

  @Autowired
  P2sHttpsInitializer _httpsInit;

  @Autowired
  ConnectFailAdder _failAdder;

  @Autowired
  StatsItemGetter _itemGetter;

  @Autowired
  ActorTeller _actorTeller;
}
